package com.ocean.web.util;
import ucar.ma2.Array;
import ucar.ma2.DataType;
import ucar.ma2.InvalidRangeException;
import ucar.ma2.Range;
import ucar.nc2.*;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;

import java.io.*;
import java.util.*;

public class CpjypgXBDQSZ {
    // 目标经纬度范围
    private static final double MIN_LAT = 14.0;
    private static final double MAX_LAT = 42.0;
    private static final double MIN_LON = 105.0;
    private static final double MAX_LON = 132.0;

    public static void main(String[] args) {
        // 原tar包路径和输出NC文件路径（替换为实际路径）
        String tarInputPath = "E:\\项目资料备份\\新云平台文件\\2025工作\\样例数据-检验\\西北太大气数值产品\\NMF_BEN_SA_NWPACDT_2025090912_168h_ATM.tar";
        String outputNcPath = "E:\\NMF_BEN_SA_NWPACDT_2025090912_168h_ATM_cropped.nc";
    }
    public static String CJNcdatazgjh(String tarInputPath, String outputNcPath){
        try {
            // 1. 扫描tar包获取目标文件
            Map<String, TarEntryInfo> targetEntries = scanTarWithCompatibleOffset(tarInputPath);
            if (targetEntries.size() != 3) {
                throw new RuntimeException("未找到完整的SLP、U10、V10文件");
            }
            System.out.println("识别目标文件：" + targetEntries.keySet());

            try (RandomAccessFile raf = new RandomAccessFile(tarInputPath, "r")) {
                // 读取SLP文件（提取time维度坐标）
                TarEntryInfo slpEntry = targetEntries.get("SLP");
                NetcdfFile slpNc = readNcFromTar(raf, slpEntry);
                Variable slpVar = slpNc.findVariable("SLP");
                Variable latVar = slpNc.findVariable("latitude");
                Variable lonVar = slpNc.findVariable("longitude");
                validateCoreVariables(slpVar, latVar, lonVar); // 校验逻辑已修改

                // 从SLP变量中提取time维度信息
                List<Dimension> slpDims = slpVar.getDimensions();
                int timeSize = slpDims.get(0).getLength();
                Array timeData = extractTimeCoordinates(slpVar, slpNc, timeSize);
                String timeDimName = slpDims.get(0).getShortName();

                // 经纬度裁剪（支持FLOAT/DOUBLE类型）
                double[] lats = readLatLonArray(latVar); // 统一转换为double[]
                double[] lons = readLatLonArray(lonVar);
                int[] latIndices = getRangeIndices(lats, MIN_LAT, MAX_LAT);
                int[] lonIndices = getRangeIndices(lons, MIN_LON, MAX_LON);
                if (latIndices == null || lonIndices == null) {
                    throw new RuntimeException("目标范围超出数据范围");
                }
                int latStart = latIndices[0], latCount = latIndices[1];
                int lonStart = lonIndices[0], lonCount = lonIndices[1];
                System.out.printf("裁剪参数：time=%d, lat=[%d:%d], lon=[%d:%d]%n",
                        timeSize, latStart, latStart + latCount - 1, lonStart, lonStart + lonCount - 1);

                // 裁剪数据
                int[] varOrigin = new int[]{0, latStart, lonStart};
                int[] varShape = new int[]{timeSize, latCount, lonCount};
                Array croppedLat = cropVariable(latVar, new int[]{latStart}, new int[]{latCount});
                Array croppedLon = cropVariable(lonVar, new int[]{lonStart}, new int[]{lonCount});
                Array croppedSlp = cropVariable(slpVar, varOrigin, varShape);

                // 读取U10并裁剪
                TarEntryInfo u10Entry = targetEntries.get("U10");
                NetcdfFile u10Nc = readNcFromTar(raf, u10Entry);
                Variable u10Var = u10Nc.findVariable("U10");
                validateVariableDimensions(u10Var, timeSize, latVar.getShape()[0], lonVar.getShape()[0]);
                Array croppedU10 = cropVariable(u10Var, varOrigin, varShape);

                // 读取V10并裁剪
                TarEntryInfo v10Entry = targetEntries.get("V10");
                NetcdfFile v10Nc = readNcFromTar(raf, v10Entry);
                Variable v10Var = v10Nc.findVariable("V10");
                validateVariableDimensions(v10Var, timeSize, latVar.getShape()[0], lonVar.getShape()[0]);
                Array croppedV10 = cropVariable(v10Var, varOrigin, varShape);

                // 生成带独立time变量的新NC文件
                createMergedNcWithTime(outputNcPath, slpNc, slpVar,
                        latVar, lonVar, u10Var, v10Var, timeDimName,
                        croppedLat, croppedLon, timeData,
                        croppedSlp, croppedU10, croppedV10,
                        timeSize, latCount, lonCount);

                // 关闭资源
                slpNc.close();
                u10Nc.close();
                v10Nc.close();
            }
            System.out.println("处理完成！新文件：" + outputNcPath);
            return outputNcPath;
        } catch (Exception e) {
            System.err.println("处理失败：" + e.getMessage());
            e.printStackTrace();
            return "500";
        }
    }


    /**
     * 读取经纬度数组（兼容FLOAT和DOUBLE类型，统一返回double[]）
     */
    private static double[] readLatLonArray(Variable var) throws IOException {
        Array array = var.read();
        DataType type = var.getDataType();
        if (type == DataType.DOUBLE) {
            return (double[]) array.copyTo1DJavaArray();
        } else if (type == DataType.FLOAT) {
            float[] floatArray = (float[]) array.copyTo1DJavaArray();
            double[] doubleArray = new double[floatArray.length];
            for (int i = 0; i < floatArray.length; i++) {
                doubleArray[i] = floatArray[i]; // 转换为double
            }
            return doubleArray;
        } else {
            throw new RuntimeException("经纬度变量类型不支持（仅支持FLOAT/DOUBLE），实际类型：" + type);
        }
    }

    /**
     * 校验核心变量（允许经纬度为FLOAT或DOUBLE）
     */
    private static void validateCoreVariables(Variable slpVar, Variable latVar, Variable lonVar) {
        // 校验变量存在性
        if (slpVar == null || latVar == null || lonVar == null) {
            throw new RuntimeException("核心变量缺失（SLP/latitude/longitude）");
        }

        // 校验SLP是3维变量（time→lat→lon）
        if (slpVar.getRank() != 3) {
            throw new RuntimeException("SLP必须是3维变量（time, latitude, longitude），当前维度数：" + slpVar.getRank());
        }

        // 校验lat/lon是1维变量
        if (latVar.getRank() != 1 || lonVar.getRank() != 1) {
            throw new RuntimeException("latitude/longitude必须是1维变量");
        }

        // 校验SLP类型为FLOAT
        if (slpVar.getDataType() != DataType.FLOAT) {
            throw new RuntimeException("SLP变量类型必须为FLOAT，实际类型：" + slpVar.getDataType());
        }

        // 校验经纬度类型为FLOAT或DOUBLE
        DataType latType = latVar.getDataType();
        DataType lonType = lonVar.getDataType();
        if (latType != DataType.FLOAT && latType != DataType.DOUBLE) {
            throw new RuntimeException("latitude变量类型必须为FLOAT或DOUBLE，实际类型：" + latType);
        }
        if (lonType != DataType.FLOAT && lonType != DataType.DOUBLE) {
            throw new RuntimeException("longitude变量类型必须为FLOAT或DOUBLE，实际类型：" + lonType);
        }
    }

    // 以下方法与之前版本一致，仅保留核心修改部分
    private static Array extractTimeCoordinates(Variable slpVar, NetcdfFile ncFile, int timeSize) throws IOException {
        Attribute coordAttr = slpVar.findAttribute("coordinates");
        if (coordAttr != null) {
            String[] coordNames = coordAttr.getStringValue().split(" ");
            for (String name : coordNames) {
                Variable timeVar = ncFile.findVariable(name);
                if (timeVar != null && timeVar.getRank() == 1 && timeVar.getShape()[0] == timeSize) {
                    System.out.println("从坐标属性找到time变量：" + name);
                    return timeVar.read();
                }
            }
        }

        Dimension timeDim = slpVar.getDimensions().get(0);
        Attribute timeUnitsAttr = slpVar.findAttribute("time_units");
        if (timeUnitsAttr != null) {
            System.out.println("从维度属性生成time坐标，单位：" + timeUnitsAttr.getStringValue());
            double[] timeValues = new double[timeSize];
            for (int i = 0; i < timeSize; i++) {
                timeValues[i] = i;
            }
            return Array.factory(DataType.DOUBLE, new int[]{timeSize}, timeValues);
        }

        System.out.println("未找到time坐标信息，用索引值填充");
        double[] timeValues = new double[timeSize];
        for (int i = 0; i < timeSize; i++) {
            timeValues[i] = i;
        }
        return Array.factory(DataType.DOUBLE, new int[]{timeSize}, timeValues);
    }

    private static void createMergedNcWithTime(String outputPath, NetcdfFile refNc, Variable slpVar,
                                               Variable latVar, Variable lonVar, Variable u10Var, Variable v10Var,
                                               String timeDimName,
                                               Array croppedLat, Array croppedLon, Array timeData,
                                               Array croppedSlp, Array croppedU10, Array croppedV10,
                                               int timeSize, int newLatSize, int newLonSize) throws IOException {

        NetcdfFileWriter writer = NetcdfFileWriter.createNew(NetcdfFileWriter.Version.netcdf4, outputPath);
        try {
            Dimension timeDim = writer.addDimension(null, timeDimName, timeSize);
            Dimension latDim = writer.addDimension(null, latVar.getDimensions().get(0).getShortName(), newLatSize);
            Dimension lonDim = writer.addDimension(null, lonVar.getDimensions().get(0).getShortName(), newLonSize);
            List<Dimension> dataDims = Arrays.asList(timeDim, latDim, lonDim);

            Variable newTimeVar = writer.addVariable(null, timeDimName, DataType.DOUBLE, timeDimName);
            copyTimeAttributes(slpVar, newTimeVar);

            // 保留原经纬度类型（FLOAT/DOUBLE）
            Variable newLatVar = writer.addVariable(null, latVar.getShortName(), latVar.getDataType(), latDim.getShortName());
            copyVariableAttributes(latVar, newLatVar);
            Variable newLonVar = writer.addVariable(null, lonVar.getShortName(), lonVar.getDataType(), lonDim.getShortName());
            copyVariableAttributes(lonVar, newLonVar);

            Variable newSlpVar = writer.addVariable(null, slpVar.getShortName(), DataType.FLOAT, dataDims);
            copyVariableAttributes(slpVar, newSlpVar);
            Variable newU10Var = writer.addVariable(null, u10Var.getShortName(), DataType.FLOAT, dataDims);
            copyVariableAttributes(u10Var, newU10Var);
            Variable newV10Var = writer.addVariable(null, v10Var.getShortName(), DataType.FLOAT, dataDims);
            copyVariableAttributes(v10Var, newV10Var);

            for (Attribute attr : refNc.getGlobalAttributes()) {
                writer.addGlobalAttribute(attr);
            }

            writer.create();
            writer.write(newTimeVar, timeData);
            writer.write(newLatVar, croppedLat);
            writer.write(newLonVar, croppedLon);
            writer.write(newSlpVar, croppedSlp);
            writer.write(newU10Var, croppedU10);
            writer.write(newV10Var, croppedV10);

            System.out.println("新NC文件已包含独立time变量，经纬度类型：" + latVar.getDataType());

        } catch (InvalidRangeException e) {
            throw new RuntimeException(e);
        } finally {
            writer.close();
        }
    }

    // 其他复用方法（与之前版本一致）
    static class TarEntryInfo {
        long headerOffset;
        long dataSize;
        String fileName;

        TarEntryInfo(long headerOffset, long dataSize, String fileName) {
            this.headerOffset = headerOffset;
            this.dataSize = dataSize;
            this.fileName = fileName;
        }
    }

    private static Map<String, TarEntryInfo> scanTarWithCompatibleOffset(String tarPath) throws IOException {
        Map<String, TarEntryInfo> targetEntries = new HashMap<>(3);
        try (FileInputStream fis = new FileInputStream(tarPath);
             TarArchiveInputStream tais = new TarArchiveInputStream(fis)) {

            TarArchiveEntry entry;
            long currentStreamPos = 0;

            while ((entry = tais.getNextTarEntry()) != null) {
                long entryHeaderOffset = currentStreamPos;

                if (entry.isDirectory() || entry.getSize() <= 0 || !entry.getName().endsWith(".nc")) {
                    currentStreamPos += calculateEntryTotalSize(entry);
                    continue;
                }

                String entryName = entry.getName();
                if (entryName.contains("SLP") && !targetEntries.containsKey("SLP")) {
                    targetEntries.put("SLP", new TarEntryInfo(entryHeaderOffset, entry.getSize(), entryName));
                } else if (entryName.contains("U10") && !targetEntries.containsKey("U10")) {
                    targetEntries.put("U10", new TarEntryInfo(entryHeaderOffset, entry.getSize(), entryName));
                } else if (entryName.contains("V10") && !targetEntries.containsKey("V10")) {
                    targetEntries.put("V10", new TarEntryInfo(entryHeaderOffset, entry.getSize(), entryName));
                }

                if (targetEntries.size() == 3) break;
                currentStreamPos += calculateEntryTotalSize(entry);
            }
        }
        return targetEntries;
    }

    private static long calculateEntryTotalSize(TarArchiveEntry entry) {
        long alignedDataSize = (entry.getSize() + 511) / 512 * 512;
        return 512 + alignedDataSize;
    }

    private static NetcdfFile readNcFromTar(RandomAccessFile raf, TarEntryInfo entryInfo) throws IOException {
        long dataStartPos = entryInfo.headerOffset + 512;
        System.out.printf("读取文件：%s，位置：%d，大小：%d字节%n",
                entryInfo.fileName, dataStartPos, entryInfo.dataSize);

        byte[] ncData = new byte[(int) entryInfo.dataSize];
        raf.seek(dataStartPos);
        int totalBytesRead = 0;
        while (totalBytesRead < ncData.length) {
            int bytesRead = raf.read(ncData, totalBytesRead, ncData.length - totalBytesRead);
            if (bytesRead == -1) throw new IOException("读取tar数据失败");
            totalBytesRead += bytesRead;
        }

        File tempFile = File.createTempFile("nc_compat_", ".nc");
        tempFile.deleteOnExit();
        try (FileOutputStream fos = new FileOutputStream(tempFile)) {
            fos.write(ncData);
        }

        if (tempFile.length() != entryInfo.dataSize) throw new IOException("临时文件写入错误");
        NetcdfFile ncFile = NetcdfFile.open(tempFile.getAbsolutePath());
        if (ncFile.getVariables().isEmpty()) throw new IOException("无效NC文件：" + entryInfo.fileName);
        return ncFile;
    }

    private static void validateVariableDimensions(Variable var, int expectedTimeSize, int expectedLatSize, int expectedLonSize) {
        if (var == null) throw new RuntimeException("变量缺失（U10/V10）");
        if (var.getRank() != 3) throw new RuntimeException(var.getShortName() + "必须是3维变量");
        if (var.getDataType() != DataType.FLOAT) throw new RuntimeException(var.getShortName() + "类型必须为FLOAT");

        List<Dimension> dims = var.getDimensions();
        if (dims.get(0).getLength() != expectedTimeSize ||
                dims.get(1).getLength() != expectedLatSize ||
                dims.get(2).getLength() != expectedLonSize) {
            throw new RuntimeException(var.getShortName() + "维度不匹配");
        }
    }

    private static int[] getRangeIndices(double[] array, double min, double max) {
        int startIdx = -1, endIdx = -1;
        boolean isAscending = array.length > 1 && array[array.length - 1] > array[0];

        for (int i = 0; i < array.length; i++) {
            double val = array[i];
            boolean inRange = (isAscending && val >= min && val <= max) || (!isAscending && val <= min && val >= max);
            if (inRange) {
                if (startIdx == -1) startIdx = i;
                endIdx = i;
            }
        }

        return (startIdx == -1 || endIdx == -1) ? null : new int[]{startIdx, endIdx - startIdx + 1};
    }

    private static Array cropVariable(Variable var, int[] origin, int[] shape) throws IOException, InvalidRangeException {
        Range[] ranges = new Range[origin.length];
        for (int i = 0; i < origin.length; i++) {
            ranges[i] = new Range(origin[i], origin[i] + shape[i] - 1);
        }
        return var.read(Arrays.asList(ranges));
    }

    private static void copyTimeAttributes(Variable srcVar, Variable timeVar) {
        String[] timeAttrs = {"units", "calendar", "long_name", "standard_name", "time_origin"};
        for (String attrName : timeAttrs) {
            Attribute attr = srcVar.findAttribute(attrName);
            if (attr != null) {
                timeVar.addAttribute(attr);
            }
        }
        if (timeVar.findAttribute("units") == null) {
            timeVar.addAttribute(new Attribute("units", "hours since 2025-09-09 00:00:00"));
        }
    }

    private static void copyVariableAttributes(Variable srcVar, Variable destVar) {
        for (Attribute attr : srcVar.getAttributes()) {
            destVar.addAttribute(attr);
        }
    }
}